/**
 * random test functions for card unit tests
 * Team 15, 3/12/14
 *
 */


#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "rngs.h"
#include <math.h>
#include "randomCardUnitTests.h"
#include "cardUnitTestHelpers.h"

/**
 *  Random test function for adventurer card
 *
 */
int randomTestAdventurer(){
    int i, j, n, p, randPos;

  //int k[10] = {adventurer, council_room, feast, gardens, mine,
    //       remodel, smithy, village, baron, great_hall};         

  //initialize test variables        
  int testCount = 0;
  int passCount = 0;
  int intcompleteTestCount = 0;
  int wrongTreasureCount = 0;
  int wrongDrawCount = 0;

  struct gameState G;
  
  printf ("Testing cardEffect for adventurer card.\n");

  printf ("RANDOM TESTS.\n\n");

  printf ("Any test failures will be detailed below.\n"
  "SUMMARY RESULTS APPEAR AT END OF REPORT\n\n");

  //initialize random generator
  SelectStream(2);
  PutSeed(3);
  
  //perform 8000 random tests
  for (n = 0; n < 8000; n++) {

    //fill gamestate with random values
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    
    //select a random player (0-3)
    p = floor(Random() * 4);
    assert(p != 4);
    
    //manually initialize game
    G.deckCount[p] = floor(Random() * (MAX_DECK + 1));
    G.discardCount[p] = floor(Random() * (MAX_DECK + 1));
    G.handCount[p] = floor(Random() * (MAX_HAND) + 1);
    G.whoseTurn = p;
    G.playedCardCount = 0;
    for (j = 0; j <G.deckCount[p]; j++){
      G.deck[p][j] = floor(Random() * treasure_map + 1);
    }
    for (j = 0; j <G.discardCount[p]; j++){
      G.discard[p][j] = floor(Random() * treasure_map + 1);
    }
    for (j = 0; j <G.handCount[p]; j++){
        G.hand[p][j] = floor(Random() * treasure_map + 1);
    }
    //make sure adventurer card is in hand
    randPos = floor(Random() * (G.handCount[p]));    
    G.hand[p][randPos] = adventurer;   

    //run test
    int * results = checkAdventurerEffect(&G, p, randPos);
    
    //parse results
    int resultsSize = 3;
    int pass = 1;
    int l;
    for (l = 0; l < resultsSize; l++){
      if (results[l] == 0){
        pass = 0;
        if(l == 0){
          intcompleteTestCount++;
        }
        else if (l == 1){
          wrongTreasureCount++;
        }
        else if (l == 2){
          wrongDrawCount++;
        }
      }
    }
    //track pass/fail
    if (pass == 1){
      passCount++;
    }
    else{
      printf("Test No. %d\n\n", testCount + 1);
    }
    testCount++;  
        
  }

  //print global results
  if (passCount == testCount){

    printf ("ALL TESTS OK\n");

  }
  else{

    printf("===============================================\n");

    printf("OVERALL RESULTS: %d TESTS PERFOMRED, %d TESTS PASSED\n\n", testCount, passCount);
    printf("Reasons for failures:\n1.  Test did not complete: %d test runs\n"
        "2.  Final 2 cards in player's hand not treasure cards: %d test runs\n"
        "3.  Player's handCount did not increase by 1 card: %d test runs\n\n",
        intcompleteTestCount, wrongTreasureCount, wrongDrawCount);

  }
  
  return 0;
}

/**
 * helper for random adventurer card tests
 * 
 *
 */
int* checkAdventurerEffect(struct gameState *post, int p, int randPos) {
  int * result = malloc(sizeof(int) * 3);
  struct gameState pre;
  memcpy (&pre, post, sizeof(struct gameState));

  int r, i; 

  //initialize result array
  for (i = 0; i < 3; i++){
    result[i] = 1;
  }

  //call cardEffect
  r = cardEffect (adventurer, 0, 0, 0, post, randPos, 0);

  //check that final 2 cards are treasure cards after cardEffect
  int treasureCount = 0;
  int lastCards = 2;
  for (i = 1; i <= lastCards; i++){
    int checkCard = post->hand[p][post->handCount[p] - i];
    if (checkCard == copper || checkCard == silver || checkCard == gold){
      treasureCount++;
    }
  }
  
  //check that only 1 total card was retained in hand after cardEffect
  int checkDrawnCards = 0;
  int preCount = pre.handCount[p];
  int postCount = post->handCount[p];
  if (preCount + 1 == postCount){
    checkDrawnCards = 1;
  }

  //set results array
  int pass = 1;
  if (r != 0){
    result[0] = 0;
    pass = 0;
  }
  if (treasureCount != 2){
    result[1] = 0;
    pass = 0;
  }
  if (!(checkDrawnCards)){
    result[2] = 0;
    pass = 0;
  }

  //print detailed results
  if (pass == 0){
    printf("TEST FAILED\nGamestate:\n"
        "Player: %d\n"
        "starting deckCount: %d\n"
        "starting discardCount: %d\n"
        "starting handCount: %d\n"
        "ending deckCount %d\n"
        "ending discardCount %d\n"
        "ending handCount %d\n"                
        "Number of last 2 cards in hand that are treasure cards\n after playing adventurer card: %d\n"
        "Number of cards added to hand by playing adventurer card: %d\n", 
        p, pre.deckCount[p], pre.discardCount[p], pre.handCount[p], 
        post->deckCount[p], post->discardCount[p], post->handCount[p],
        treasureCount, (postCount - preCount));
  }
  
  return result;  
}



/**
 *  Random tests for council room card
 *  uses helper function checkCouncilRoomEffect
 *
 */
 int randomTestCouncilRoom(){
  int i, j, n, p, randPos;

  //initialize gameplay variables      
  int testCount = 0;
  int passCount = 0;
  int intcompleteTestCount = 0;
  int wrongDrawCount = 0;
  int wrongDrawCountOtherPlayer = 0;
  int wrongDiscard = 0;
  int wrongNumBuysCount = 0;

  struct gameState G;
  
  printf ("Testing cardEffect for council_room card.\n");

  printf ("RANDOM TESTS.\n\n");
  printf ("Any test failures will be detailed below.\n"
    "SUMMARY RESULTS APPEAR AT END OF REPORT\n\n");

  //initialize random generator
  SelectStream(2);
  PutSeed(3);

  //perform 8000 tests
  for (n = 0; n < 8000; n++) {

    //fill gamestate struct with random numbers
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    
    //set number of players
    G.numPlayers = floor(Random() * 4) + 1;

    //choose player to play card
    p = floor(Random() * G.numPlayers);    
    G.whoseTurn = p;
    
    //initialize all players
    for (i = 0; i < G.numPlayers; i++){
      //deck containing 0 - MAX_DECK cards
      G.deckCount[i] = floor(Random() * (MAX_DECK + 1));

      //discard pile containing 0 - MAX_DECK cards
      G.discardCount[i] = floor(Random() * (MAX_DECK + 1));
      
      //hand containing at least 1 card (since we are playing adventurer card)
      G.handCount[i] = floor(Random() * (MAX_HAND) + 1) ;
       
      for (j = 0; j <G.deckCount[i]; j++){
        G.deck[i][j] = floor(Random() * treasure_map + 1);
      }
      for (j = 0; j <G.discardCount[i]; j++){
        G.discard[i][j] = floor(Random() * treasure_map + 1);
      }
      for (j = 0; j <G.handCount[i]; j++){
        G.hand[i][j] = floor(Random() * treasure_map + 1);
      }
    }
    //initialize playedCardCount
    G.playedCardCount = 0;

    //ensure playedCardCount is not maxed out
    G.playedCardCount = floor(Random() * (MAX_DECK + 1));
    if (G.playedCardCount == MAX_DECK){
      G.playedCardCount--;
    }
    

    //make sure adventurer card is in hand
    randPos = floor(Random() * (G.handCount[p]));    
    G.hand[p][randPos] = council_room;
    
    //parse results    
    int * results = checkCouncilRoomEffect(&G, p, randPos);
    int resultsSize = 5;
    int pass = 1;
    int l;
    for (l = 0; l < resultsSize; l++){
      if (results[l] == 0){
        pass = 0;
        if(l == 0){
          intcompleteTestCount++;
        }
        else if (l == 1){
          wrongDrawCount++;
        }
        else if (l == 2){
          wrongDrawCountOtherPlayer++;
        }
        else if (l == 3){
          wrongDiscard++; 
        }
        else if (l == 4){
          wrongNumBuysCount++;
        }
      }
    }

    //check pass/fail
    if (pass == 1){
      passCount++;
    }
    else{
      printf("Test No. %d\n\n", testCount + 1);
    }
    testCount++;  
        
  }


  //print global results
  if (passCount == testCount){

    printf ("ALL TESTS OK\n");

  }
  else{
    printf("===============================================\n");

    printf("OVERALL RESULTS: %d TESTS PERFOMRED, %d TESTS PASSED\n\n", testCount, passCount);
    printf("Reasons for failures:\n1.  Test did not complete: %d test runs\n"
        "2.  Player did not draw a net of 3 cards: %d test runs\n"
        "3.  Opponents did not draw 1 card: %d test runs\n"
        "4.  Council room card not on top of discard pile: %d\n"
        "5.  Numbuys was not incremented: %d\n\n",
        intcompleteTestCount, wrongDrawCount, wrongDrawCountOtherPlayer, wrongDiscard, wrongNumBuysCount);

  }
  
  return 0;
}


/**
 *  Helper function for random council room tests
 *
 *
 */
int* checkCouncilRoomEffect(struct gameState *post, int p, int randPos){
  int * result = malloc(sizeof(int) * 4);
  struct gameState pre;
  memcpy (&pre, post, sizeof(struct gameState));

  int r, i; 

  //initialize result array
  for (i = 0; i < 5; i++){
    result[i] = 1;
  }

  //play council_room card
  r = cardEffect (council_room, 0, 0, 0, post, randPos, 0);

  //check that player has gained net 3 cards after playing council_room
  int checkDrawnCards = 0;
  int preCount = pre.handCount[p];
  int postCount = post->handCount[p];
  if (preCount + 3 == postCount){
    checkDrawnCards = 1;
  }
  
  //check that other players each gained a card after playing concil_room
  int checkDrawnCardsOtherPlayer = 1;  
  int v;  
  for (v = 0; v < post->numPlayers; v++){
    if (v != p){
      int preCountOtherPlayer = pre.handCount[v];
      int postCountOtherPlayer = post->handCount[v];
      if (preCountOtherPlayer + 1 != postCountOtherPlayer){
        if(post->deckCount[v] != 0){
        printf("Opposing player %d drew %d cards", v, postCountOtherPlayer - preCountOtherPlayer);
        printf("deckCount for player %d was %d\n", v, post->deckCount[v]);
        checkDrawnCardsOtherPlayer = 0;
      }
    }
  }
  }
  
  //check that council_room card is on top of discard pile (probably has been discarded)
  int checkDiscard = 0;
  int topCard = post->playedCards[post->playedCardCount - 1];
  if (topCard == council_room){
    checkDiscard = 1;
  }

  //check that numBuys incremented properly
  int validNumBuys = 1;
  int preNumBuys = pre.numBuys;
  int postNumBuys = post->numBuys;
  if (postNumBuys != (preNumBuys + 1)){
    validNumBuys = 0;
  }

  //store results in array and determine pass/fail
  int pass = 1;
  if (r != 0){
    result[0] = 0;
    pass = 0;
  }
  if (!(checkDrawnCards)){
    result[1] = 0;
    pass = 0;
  }
  if (!(checkDrawnCardsOtherPlayer)){
    result[2] = 0;
    pass = 0;
  }
  if (!(checkDiscard)){
    result[3] = 0;
    pass = 0;
  }
  if (!(validNumBuys)){
    result[4] = 0;
    pass = 0;
  }

  //print results if failed
  if (pass == 0){
    printf("\nTEST FAILED\nGamestate:\n"
        "Player: %d\n"
        "starting deckCount: %d\n"
        "starting discardCount: %d\n"
        "starting handCount: %d\n"
        "ending deckCount %d\n"
        "ending discardCount %d\n"
        "ending handCount %d\n"                
        "net number of cards drawn by player: %d\n"
        "1 of card drawn by all opposing players? (1 = yes, 0 = no): %d\n"
        "topCard on discard pile: %s\n"
        "Numbuys properly incremented? (1 = yes, 0 = no): %d\n\n", 
        p, pre.deckCount[p], pre.discardCount[p], pre.handCount[p], 
        post->deckCount[p], post->discardCount[p], post->handCount[p],
        (postCount - preCount), result[2], getCard(topCard),result[4]);
  }
  
  return result;  
}

/**
 * Random tester for village
 * uses helper function checkVillage()
 *
 */

 int randomTestVillage(){

  int i, n, numPlayers, currentPlayer, seed, testCardPos, success, failure;

  int k[10] = {adventurer, council_room, feast, gardens, mine,
           remodel, smithy, village, baron, great_hall};

  struct gameState G;
  
  printf ("***RANDOM TESTS OF VILLAGE CARD***\n");

  success = 0;
  failure = 0;
  PutSeed(floor(Random() * MODULUS));

  for (n = 0; n < 2000; n++) {
    /* Fill the Game State with garbage */
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    //initialize new game
    numPlayers = floor(Random() * PLAYER_RANGE) + 2;
    seed = floor(Random() * 1000);
    initializeGame(numPlayers, k, seed, &G);
    
    for (i = 0; i < numPlayers; i++){
      G.deckCount[i] = floor(Random() * MAX_DECK) + 5; //enough cards to draw from
      G.discardCount[i] = floor(Random() * MAX_DECK);
      G.handCount[i] = floor(Random() * MAX_HAND);
    }
    currentPlayer = floor(Random() * numPlayers); //define a current random player
    G.whoseTurn = currentPlayer; //set the game's current player to selected player
    testCardPos = floor(Random() * G.handCount[currentPlayer]); //define a random card in the current player's hand to test
    G.hand[currentPlayer][testCardPos] = village; //set that card to a smithy
    if(checkVillage(testCardPos, &G) == 0){
      success++;
    }
    else{
      failure++;
    }
  }

  printf ("\n***RANDOM TESTS OF VILLAGE CARD COMPLETE***\n***SUMMARY\n***Successful Tests: %d\n***Failed Tests: %d\n\n", success, failure); 

  return 0;


 }


int checkVillage(int testCardPos, struct gameState *testState) {
  int cardValue, functionError;
  struct gameState controlState;
  int bonus = 0;
  
  memcpy (&controlState, testState, sizeof(struct gameState));
  cardValue = testState->hand[testState->whoseTurn][testCardPos];
  //printf("***RELEVANT DATA FROM THE TEST***\n");
  //printf("Current Card Value in test = %d\n\n", cardValue);

  functionError = cardEffect (cardValue, 0, 0, 0, testState, testCardPos, &bonus);

  /* SIMULATE THE CARD EFFECT */
  drawCard(controlState.whoseTurn, &controlState); //draw one card
  controlState.numActions += 2; //gain two actions
  discardCard(testCardPos, controlState.whoseTurn, &controlState, 0); //discard
  
  assert(functionError == 0); //no error playing the card
  
  //test to see if both the states are the same
  if(memcmp(&controlState, testState, sizeof(struct gameState)) == 0){
    return 0;
  }
  else{
    gameStateDiff(&controlState, testState);
    return 1;
  }
}